using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Perovich.GameObjects.Interfaces;



namespace Perovich.GameObjects
{
    /// <summary>
    /// Extension of game usable with <see cref="Component"/>s.
    /// </summary>
    public class ObjectGame : Game, ICanHoldComponents
    {

        #region ICanHoldComponents Members
        /// <summary>
        /// Gets the <see cref="ContentManager"/> associated with this <see cref="ObjectGame"/>.
        /// </summary>
        public new ContentManager Content
        {
            get
            {
                return base.Content;
            }
            set
            {
                base.Content = value;
            }
        }


        /// <summary>
        /// Gets the <see cref="IServiceProvider"/> for this <see cref="ObjectGame"/>.
        /// </summary>
        public new IServiceProvider Services
        {
            get { return base.Services; }
        }



        ComponentCollection _comps = new ComponentCollection();

        /// <summary>
        /// Gets the collection of components this <see cref="ObjectGame"/> manages.
        /// </summary>
        ComponentCollection ICanHoldComponents.Components
        {
            get { return _comps; }
        }

        /// <summary>
        /// This property is not to be used. Use the explicit interface implementation <see cref="ICanHoldComponents.Components"/> instead.
        /// </summary>
        public new GameComponentCollection Components
        {
            get
            {
                throw new InvalidOperationException("ObjectGame.Components is not to be used, use ObjectGame.ICanHoldComponents.Components instead.");
            }
        }


        /// <summary>
        /// Called after the <see cref="ObjectGame"/> and <see cref="GraphicsDevice"/> are created, but before <see cref="ObjectGame.LoadContent"/>. 
        /// </summary>
        protected override void Initialize()
        {
            me.Components.Initialize();
            base.Initialize();
        }

        ICanHoldComponents me;


        /// <summary>
        /// Called when the <see cref="ObjectGame"/> has determined that game logic needs to be processed. Override this method with game specific update code.
        /// </summary>
        /// <param name="gameTime">Time passed since the last call to <see cref="ObjectGame.Update"/>.</param>
        protected override void Update(GameTime gameTime)
        {
            me.Components.Update(gameTime);
            base.Update(gameTime);
            Perovich.GameObjects.ComponentsExtensions.CheckColisions(this);
        }

        /// <summary>Called before the <see cref="Draw"/> function. Allows any components to draw the contents of any render targets they use.</summary>
        /// <param name="gameTime">Provides a snapshot of the game's timing values.</param>
        protected virtual void DrawOnTargets(GameTime gameTime)
        {
            me.Components.DrawOnTargets(gameTime);
        }


        /// <summary>
        /// Called when the <see cref="ObjectGame"/> determines it is time to draw a frame. Override this method with game-specific rendering code. 
        /// </summary>
        /// <param name="gameTime">Time passed since the last call to <see cref="ObjectGame.Draw"/>.</param>
        protected override void Draw(GameTime gameTime)
        {
            DrawOnTargets(gameTime);
            GraphicsDevice.Clear(BackgroundColor);
            me.Components.Draw(gameTime, GraphicsDevice, SBatches);
            base.Draw(gameTime);
        }

        private SpriteBatchCollection _SBatches;
        /// <summary>
        /// Triggered whenever the <see cref="SBatches"/> property is changed.
        /// </summary>
        public event EventHandler<EventArgs> SpriteBatchSet;

        private void SignalSpriteBatch()
        {
            if (SpriteBatchSet != null)
            {
                SpriteBatchSet(this, null);
            }
        }

        /// <summary>
        /// Gets or sets the <see cref="SpriteBatch"/> used to draw this <see cref="ObjectGame"/>.
        /// </summary>
        public SpriteBatchCollection SBatches
        {
            get { return _SBatches; }
            set
            {
                SpriteBatchCollection old = _SBatches;
                _SBatches = value;
                if (_SBatches != old)
                    SignalSpriteBatch();
            }
        }


        /// <summary>
        /// The <see cref="Color"/> of the game's background.
        /// </summary>
        public Color BackgroundColor
        {
            get;
            set;
        }


        #endregion

        private IGraphicsDeviceService graphics;
        /// <summary>
        /// Gets the <see cref="IGraphicsDeviceService"/> associated with this <see cref="ObjectGame"/>.
        /// </summary>
        protected IGraphicsDeviceService Graphics
        {
            get { return graphics; }
            set { graphics = value; }
        }
        /// <summary>
        /// Constructs a new instance of the <see cref="ObjectGame"/> class.
        /// </summary>
        public ObjectGame()
        {
            me = this;
            graphics = new GraphicsDeviceManager(this);
        }
        /// <summary>
        ///   Called when graphics resources need to be loaded. Override this method to load
        ///   any game-specific graphics resources.
        /// </summary>
        protected override void LoadContent()
        {
            SBatches = new SpriteBatchCollection(GraphicsDevice);
            base.LoadContent();
        }

        /// <summary>
        /// Performs appllication-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <param name="disposing">A value indicating whether to release managed resources.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                me.Components.Dispose();
                (graphics as IDisposable).Dispose();
            }
            base.Dispose(disposing);
        }
    }
}
